Topic: 開放資料 - 黃金價格深度學習預測應用(LSTM)¶
File: tutorial_gold_prediction_lstm.ipynb
Author: Ming-Chang Lee
Date: 2024.12.08
References:
大綱¶
- CRISP-DM 六大步驟
- 黃金價格深度學習預測應用(LSTM)
- 台灣股市,ETF下載
1. CRISP-DM 六大步驟¶
- 步驟 1:商業理解
- 步驟 2:資料理解
- 步驟 3:資料準備
- 步驟 4:建立模型
- 步驟 5:模型評估與測試
- 步驟 6:佈署應用
2. 黃金價格深度學習預測應用(LSTM)¶
步驟 1:商業理解¶
目標是使用 Yahoo 開放式財金資料, 建立深度學習預測應用(LSTM).
黃金常用投資方法
實體黃金(不建議)
黃金存摺
黃金ETF(指數股票型基金, Exchange-Traded Funds, ETF)-5種商品
(1). 黃金價格追蹤ETF: ETF主要目的是追蹤黃金價格變化, 該ETF通常會真正的持有實物黃金(如金塊或金條)來追蹤黃金價格.
(2). 黃金礦業ETF: 主要持有金礦公司的股票, 投資人投資的項目不是「實物黃金」, 而是購買挖礦業者的股票, 等於是通過黃金礦業的興衰來參與黃金市場.
(3). 國際和地區性ETF: 例: 購買美國、加拿大、澳大利亞或南非等地的黃金ETF, 是以「地區」、「國家」作為畫分區隔.
ETF範例:
- 00635U(台股): 元大S&P黃金
- 00708L(台股): 元大S&P黃金正2
- GLD: SPDR黃金指數型基金, 全球最大的黃金指數型基金
- IAU: iShares黃金信託ETF
- SLV: iShares白銀信託ETF
- GC: 紐約商業交易所的黃金期貨ETF (New York Stock Exchange, NYMEX)
- MGC: 芝加哥期貨交易所的黃金期貨ETF (Chicago Board of Trade, CBOT)
黃金ETF期貨:
- 使用黃金期貨來達成投資黃金的策略, 該ETF不會真正的持有實物黃金, 是以國際黃金市場未來某時點的黃金價格為交易標的的期貨合約, 而這類ETF由於具有時間價值的意義並具有較高的波動性.
- 本研究資料集為黃金期貨代號為 GC.
黃金期貨注意事項:
- 優點:進入門檻低, 且不需要保管成本.
- 缺點:期貨合約會到期, 需要不停更換新合約, 無法放著不理.
黃金CFD(差價合約, Contract For Difference, CFD)
黃金相關個股
黃金單位:
- 1公克 = 0.26667台錢 = 0.02667台兩
- 1盎司(ounce, 簡寫為 oz, 英兩) = 31.1035公克 = 8.29437台錢 = 0.829437台兩
- 1盎司大約等於1台兩
- 雖然黃金的英文是gold,但其在國際金融市場的簡稱卻是XAU,其中X代表它不是任何一國家發行的貨幣,AU則是黃金的化學符號。
查詢財金價格
- https://tw.stock.yahoo.com/
- 左上角輸入 GC=F
步驟 2:資料理解¶
In [1]:
# yfinance 模組
# https://pypi.org/project/yfinance/
# 安裝模組:
# pip install yfinance
# 載入模組
# Part 1. 擷取財務金融資料
# yfinance 會自動載入 pandas 模組
import yfinance as yf # yf.download 擷取財金資料
# Part 2. 資料處理
import numpy as np
# import pandas as pd
# Part 3. 資料視覺化
import plotly.express as px
import matplotlib.pyplot as plt
# Part 4. 深度學習
from sklearn.preprocessing import MinMaxScaler
from keras import Model
from keras.layers import Input, Dense, Dropout
from keras.layers import LSTM
from sklearn.metrics import mean_absolute_percentage_error
# 使用 Spyder 設定 plotly 繪圖結果顯示於瀏覽器
# import plotly.io as pio
# pio.renderers.default='browser'
# 使用 Colab, Jupyter-notebook 不用設定此選項
# matplotlib 中文字型 - 設定 matplotlib.rcParams 方法
# https://github.com/rwepa/ipas_bda/blob/main/ipas-python-program.py#L1488
from matplotlib import rcParams
rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 設定中文字型
rcParams['axes.unicode_minus'] = False # 設定負正確顯示
In [2]:
# 匯入資料
# 原參考資料使用 CSV檔案, 本例改為使用即時網路讀取黃金價格資料.
# 資料為紐約商業交易所黃金期貨ETF, 簡稱GC
# end參數為設定結束日期, 結果不包括參數日期, 本例結束日期不包括2023-12-15.
df = yf.download(tickers="GC=F", start="2013-01-01", end="2024-11-30")
# 理解財金指標使用的幣別
gold = yf.Ticker("GC=F") # Gold Futures on Yahoo Finance
gold.history_metadata # dict
print(gold.history_metadata['currency']) # USD
[*********************100%***********************] 1 of 1 completed
USD
注意: yfinance回傳結果為 MultiIndex。
In [3]:
# 資料檢視
# 2013-01-02 ~ 2023-11-29, 2997列*6行
df
Out[3]:
| Price | Adj Close | Close | High | Low | Open | Volume |
|---|---|---|---|---|---|---|
| Ticker | GC=F | GC=F | GC=F | GC=F | GC=F | GC=F |
| Date | ||||||
| 2013-01-02 | 1687.900024 | 1687.900024 | 1693.800049 | 1670.000000 | 1672.800049 | 35 |
| 2013-01-03 | 1673.699951 | 1673.699951 | 1686.800049 | 1662.000000 | 1686.099976 | 140 |
| 2013-01-04 | 1648.099976 | 1648.099976 | 1658.300049 | 1625.699951 | 1647.000000 | 199 |
| 2013-01-07 | 1645.500000 | 1645.500000 | 1659.900024 | 1643.800049 | 1656.500000 | 49 |
| 2013-01-08 | 1661.500000 | 1661.500000 | 1661.500000 | 1647.699951 | 1647.699951 | 17 |
| ... | ... | ... | ... | ... | ... | ... |
| 2024-11-25 | 2616.800049 | 2616.800049 | 2689.399902 | 2616.800049 | 2689.399902 | 94 |
| 2024-11-26 | 2620.300049 | 2620.300049 | 2625.600098 | 2620.300049 | 2625.600098 | 177858 |
| 2024-11-27 | 2639.899902 | 2639.899902 | 2657.899902 | 2627.199951 | 2633.500000 | 61653 |
| 2024-11-28 | 2639.699951 | 2639.699951 | 2648.600098 | 2620.699951 | 2636.399902 | 61653 |
| 2024-11-29 | 2657.000000 | 2657.000000 | 2664.300049 | 2620.699951 | 2636.399902 | 3861 |
2997 rows × 6 columns
問題: 結果中顯示 ... 的資料值如何顯示?
分析:
- 方法1: 在 Spyder 右上角 [Variable Explorer] 中按滑鼠左鍵二下並顯示結果.
- 方法2: 參考RWEPA: https://github.com/rwepa/ipas_bda/blob/main/ipas-python-program.py#L625
pd.set_option('display.expand_frame_repr', False)
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
In [4]:
# 資料物件
type(df)
Out[4]:
pandas.core.frame.DataFrame
In [5]:
# 行資料型態
# Adj Close float64 調整後收盤價
# Close float64 收盤價
# High float64 最高價
# Low float64 最低價
# Open float64 開盤價
# Volume int64 總量
df.dtypes
Out[5]:
Price Ticker Adj Close GC=F float64 Close GC=F float64 High GC=F float64 Low GC=F float64 Open GC=F float64 Volume GC=F int64 dtype: object
In [6]:
# 索引值
df.index
Out[6]:
DatetimeIndex(['2013-01-02', '2013-01-03', '2013-01-04', '2013-01-07',
'2013-01-08', '2013-01-09', '2013-01-10', '2013-01-11',
'2013-01-14', '2013-01-15',
...
'2024-11-18', '2024-11-19', '2024-11-20', '2024-11-21',
'2024-11-22', '2024-11-25', '2024-11-26', '2024-11-27',
'2024-11-28', '2024-11-29'],
dtype='datetime64[ns]', name='Date', length=2997, freq=None)
In [7]:
# 欄位名稱
# MultiIndex
df.columns
Out[7]:
MultiIndex([('Adj Close', 'GC=F'),
( 'Close', 'GC=F'),
( 'High', 'GC=F'),
( 'Low', 'GC=F'),
( 'Open', 'GC=F'),
( 'Volume', 'GC=F')],
names=['Price', 'Ticker'])
In [8]:
# 資料值
df.values
Out[8]:
array([[1.68790002e+03, 1.68790002e+03, 1.69380005e+03, 1.67000000e+03,
1.67280005e+03, 3.50000000e+01],
[1.67369995e+03, 1.67369995e+03, 1.68680005e+03, 1.66200000e+03,
1.68609998e+03, 1.40000000e+02],
[1.64809998e+03, 1.64809998e+03, 1.65830005e+03, 1.62569995e+03,
1.64700000e+03, 1.99000000e+02],
...,
[2.63989990e+03, 2.63989990e+03, 2.65789990e+03, 2.62719995e+03,
2.63350000e+03, 6.16530000e+04],
[2.63969995e+03, 2.63969995e+03, 2.64860010e+03, 2.62069995e+03,
2.63639990e+03, 6.16530000e+04],
[2.65700000e+03, 2.65700000e+03, 2.66430005e+03, 2.62069995e+03,
2.63639990e+03, 3.86100000e+03]])
In [9]:
# 欄位資料訊息
df.info()
<class 'pandas.core.frame.DataFrame'> DatetimeIndex: 2997 entries, 2013-01-02 to 2024-11-29 Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 (Adj Close, GC=F) 2997 non-null float64 1 (Close, GC=F) 2997 non-null float64 2 (High, GC=F) 2997 non-null float64 3 (Low, GC=F) 2997 non-null float64 4 (Open, GC=F) 2997 non-null float64 5 (Volume, GC=F) 2997 non-null int64 dtypes: float64(5), int64(1) memory usage: 163.9 KB
In [10]:
# 資料摘要
# count 個數
# mean 平均值
# std 標準差
# min 最小值
# 25% 25百分位數(Q1)
# 50% 50百分位數(Q2), 中位數 (median)
# 75% 75百分位數(Q3)
# max 最大值
df.describe()
Out[10]:
| Price | Adj Close | Close | High | Low | Open | Volume |
|---|---|---|---|---|---|---|
| Ticker | GC=F | GC=F | GC=F | GC=F | GC=F | GC=F |
| count | 2997.000000 | 2997.000000 | 2997.000000 | 2997.000000 | 2997.000000 | 2997.000000 |
| mean | 1551.721620 | 1551.721620 | 1559.282650 | 1544.210411 | 1551.794494 | 5217.011678 |
| std | 366.661100 | 366.661100 | 368.391433 | 364.834405 | 366.485507 | 28504.857640 |
| min | 1050.800049 | 1050.800049 | 1062.000000 | 1046.199951 | 1053.699951 | 0.000000 |
| 25% | 1257.300049 | 1257.300049 | 1264.099976 | 1252.400024 | 1257.900024 | 46.000000 |
| 50% | 1373.099976 | 1373.099976 | 1386.800049 | 1362.900024 | 1375.000000 | 163.000000 |
| 75% | 1823.800049 | 1823.800049 | 1829.900024 | 1814.500000 | 1821.800049 | 522.000000 |
| max | 2788.500000 | 2788.500000 | 2789.000000 | 2774.600098 | 2787.399902 | 386334.000000 |
In [11]:
# 將 index 轉換為 Date 資料行
df['Date'] = df.index
In [12]:
# 將 Date 移至第1欄位置
df = df[['Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']]
In [13]:
# 刪除 index 值
df = df.reset_index(drop=True)
df
Out[13]:
| Price | Date | Open | High | Low | Close | Adj Close | Volume |
|---|---|---|---|---|---|---|---|
| Ticker | GC=F | GC=F | GC=F | GC=F | GC=F | GC=F | |
| 0 | 2013-01-02 | 1672.800049 | 1693.800049 | 1670.000000 | 1687.900024 | 1687.900024 | 35 |
| 1 | 2013-01-03 | 1686.099976 | 1686.800049 | 1662.000000 | 1673.699951 | 1673.699951 | 140 |
| 2 | 2013-01-04 | 1647.000000 | 1658.300049 | 1625.699951 | 1648.099976 | 1648.099976 | 199 |
| 3 | 2013-01-07 | 1656.500000 | 1659.900024 | 1643.800049 | 1645.500000 | 1645.500000 | 49 |
| 4 | 2013-01-08 | 1647.699951 | 1661.500000 | 1647.699951 | 1661.500000 | 1661.500000 | 17 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 2992 | 2024-11-25 | 2689.399902 | 2689.399902 | 2616.800049 | 2616.800049 | 2616.800049 | 94 |
| 2993 | 2024-11-26 | 2625.600098 | 2625.600098 | 2620.300049 | 2620.300049 | 2620.300049 | 177858 |
| 2994 | 2024-11-27 | 2633.500000 | 2657.899902 | 2627.199951 | 2639.899902 | 2639.899902 | 61653 |
| 2995 | 2024-11-28 | 2636.399902 | 2648.600098 | 2620.699951 | 2639.699951 | 2639.699951 | 61653 |
| 2996 | 2024-11-29 | 2636.399902 | 2664.300049 | 2620.699951 | 2657.000000 | 2657.000000 | 3861 |
2997 rows × 7 columns
In [14]:
# 檢查重複值
df.duplicated().sum()
Out[14]:
0
In [15]:
# 檢查遺漏值, 各變數沒有遺漏值
df.isnull().sum().sum()
Out[15]:
0
In [16]:
# 匯出 Excel 檔案
df.to_excel("gold_price_2013_2024.xlsx")
In [17]:
# 圖1. GC價格走勢圖(2013-2024年)
fig = px.line(x=df[('Date', '')], y=df[('Close', 'GC=F')])
fig.update_traces(line_color='black')
fig.update_layout(xaxis_title="Date",
yaxis_title="Close Price",
title={'text': "圖1. GC價格走勢圖(2013-2024年)", 'x':0.5, 'y':0.95, 'xanchor':'center', 'yanchor':'top'},
plot_bgcolor='rgba(255, 215, 0, 0.5)')
fig.show()
In [18]:
# 圖2. 黃金價格盒鬚圖
# 考量僅進行各種價格的盒鬚圖, 因此使用 drop 刪除沒有使用的日期, 成交量
# melt: 融化, 將寬資料轉換為長資料, 使用 melt 目的為依照不同價格別繪製群組盒鬚圖
df_price = df[[('Open', 'GC=F'), ('High', 'GC=F'), ('Low', 'GC=F'), ('Close', 'GC=F')]].melt(var_name="price_type")
fig = px.box(df_price,
y="value",
facet_col="price_type",
color="price_type",
boxmode="overlay")
fig.update_layout(title={'text': "圖2. GC價格盒鬚圖(2013-2024年)", 'x':0.5, 'y':0.95, 'xanchor':'center', 'yanchor':'top'},
plot_bgcolor='rgba(255, 215, 0, 0.5)')
fig.show()
步驟 3:資料準備¶
In [19]:
# 將資料區分為訓練集與測試集
# 本例為時間序列資料, 資料不可以隨機抽取, 須保持原資料順序.
# 訓練集 train data: 2013-2022年
# 測試集 test data: 2023~2024年
test_size = df[df.Date.dt.year >=2023].shape[0]
test_size # 482 (16%)
Out[19]:
482
In [20]:
train_size = df.shape[0] - test_size
train_size # 2515 (84%)
Out[20]:
2515
In [21]:
train_list = ['train']*train_size
test_list = ['test']*test_size
split_list = train_list + test_list
# 新增 split 欄位以區分資料為訓練集或測試集
df['split'] = split_list
df.head()
Out[21]:
| Price | Date | Open | High | Low | Close | Adj Close | Volume | split |
|---|---|---|---|---|---|---|---|---|
| Ticker | GC=F | GC=F | GC=F | GC=F | GC=F | GC=F | ||
| 0 | 2013-01-02 | 1672.800049 | 1693.800049 | 1670.000000 | 1687.900024 | 1687.900024 | 35 | train |
| 1 | 2013-01-03 | 1686.099976 | 1686.800049 | 1662.000000 | 1673.699951 | 1673.699951 | 140 | train |
| 2 | 2013-01-04 | 1647.000000 | 1658.300049 | 1625.699951 | 1648.099976 | 1648.099976 | 199 | train |
| 3 | 2013-01-07 | 1656.500000 | 1659.900024 | 1643.800049 | 1645.500000 | 1645.500000 | 49 | train |
| 4 | 2013-01-08 | 1647.699951 | 1661.500000 | 1647.699951 | 1661.500000 | 1661.500000 | 17 | train |
In [22]:
df.tail()
Out[22]:
| Price | Date | Open | High | Low | Close | Adj Close | Volume | split |
|---|---|---|---|---|---|---|---|---|
| Ticker | GC=F | GC=F | GC=F | GC=F | GC=F | GC=F | ||
| 2992 | 2024-11-25 | 2689.399902 | 2689.399902 | 2616.800049 | 2616.800049 | 2616.800049 | 94 | test |
| 2993 | 2024-11-26 | 2625.600098 | 2625.600098 | 2620.300049 | 2620.300049 | 2620.300049 | 177858 | test |
| 2994 | 2024-11-27 | 2633.500000 | 2657.899902 | 2627.199951 | 2639.899902 | 2639.899902 | 61653 | test |
| 2995 | 2024-11-28 | 2636.399902 | 2648.600098 | 2620.699951 | 2639.699951 | 2639.699951 | 61653 | test |
| 2996 | 2024-11-29 | 2636.399902 | 2664.300049 | 2620.699951 | 2657.000000 | 2657.000000 | 3861 | test |
In [23]:
# 訓練集與測試集黃金價格統計圖
fig = px.line(x=df[('Date', '')], y=df[('Close', 'GC=F')], color=df[('split', '')])
fig.update_layout(xaxis_title="Date",
yaxis_title="Close Price",
title={'text': "圖3. GC價格走勢圖(2013-2013年)區分訓練集與測試集",
'x':0.5,
'y':0.95,
'xanchor':'center',
'yanchor':'top'},
plot_bgcolor='rgba(255, 215, 0, 0.5)')
fig.show()
In [24]:
# 使用 MinMaxScaler 來縮放價格以避免密集計算(intensive computations)
# MinMaxScaler 預設將資料轉換為 [0, 1]
scaler = MinMaxScaler()
# reshape(-1,1) 表示將維度改為電腦自動排列列數(-1)與1行資料, 即 reshape(列, 行)
scaler.fit(df.Close.values.reshape(-1,1))
# 建立滑動視窗(sliding window)
# 滑動視窗表示使用先前的時間序列資料來預測下一期的時間序列資料.
# 滑動視窗範例
# 考慮連續讀取2週資料, 即{1,2週}, {2,3週}, {3,4週}...
# window width(視窗寬度) = 14, 其中 Stride(步伐) = 7.
# 本研究視窗寬度設為 60, X_train 和 X_test 將是包含 60 個時間戳的收盤價list.
window_size = 60
# 原始訓練集
train_data = df.Close[:-test_size]
# 原始訓練集-資料轉換至 [0, 1]
train_data = scaler.transform(train_data.values.reshape(-1,1))
# 建立空的串列 (list)
X_train = []
y_train = []
# 使用"滑動視窗"建立訓練集
for i in range(window_size, len(train_data)):
X_train.append(train_data[i-60:i, 0])
y_train.append(train_data[i, 0])
# 檢視指標為0資料
X_train[0]
Out[24]:
array([0.36663405, 0.35846229, 0.34373019, 0.34223397, 0.35144154,
0.3475859 , 0.36053405, 0.35057833, 0.35570006, 0.36404442,
0.36364155, 0.36807274, 0.36588591, 0.36945389, 0.36571331,
0.35604533, 0.34850664, 0.34620475, 0.35098114, 0.36203027,
0.3509236 , 0.3559878 , 0.3593831 , 0.35771422, 0.36076418,
0.35656327, 0.35403117, 0.34378772, 0.34407546, 0.34148583,
0.33601883, 0.32111413, 0.31812162, 0.30315931, 0.30350458,
0.30016688, 0.30810837, 0.3247971 , 0.31328763, 0.30321685,
0.29987914, 0.29999421, 0.30143289, 0.30143289, 0.30154803,
0.30258384, 0.30327445, 0.31115841, 0.30931692, 0.31064047,
0.31173388, 0.3186971 , 0.32255281, 0.32036598, 0.32399149,
0.31961784, 0.3186971 , 0.31363297, 0.31961784, 0.3130575 ])
In [25]:
y_train[0]
Out[25]:
0.3160499318662604
In [26]:
# 原始測試集
test_data = df.Close[-test_size-60:]
# 原始測試集-資料轉換至 [0, 1]
test_data = scaler.transform(test_data.values.reshape(-1,1))
# 建立空的串列 (list)
X_test = []
y_test = []
# 使用"滑動視窗"建立測試集
for i in range(window_size, len(test_data)):
X_test.append(test_data[i-60:i, 0])
y_test.append(test_data[i, 0])
X_test[0]
Out[26]:
array([0.38033028, 0.373885 , 0.35477932, 0.36133966, 0.35650574,
0.35633307, 0.34004714, 0.34885191, 0.34424813, 0.33187545,
0.33377454, 0.34539907, 0.34407546, 0.34643495, 0.35288023,
0.35098114, 0.33883866, 0.33670944, 0.34194623, 0.34234904,
0.33176038, 0.35777175, 0.36007364, 0.38056048, 0.37940953,
0.4025436 , 0.41157851, 0.41595209, 0.41606723, 0.41560682,
0.40858607, 0.40346435, 0.39511998, 0.39563792, 0.39943603,
0.40427002, 0.39667373, 0.40145019, 0.40006904, 0.43177761,
0.42878517, 0.41238418, 0.4134776 , 0.42280024, 0.42464172,
0.43005119, 0.41992287, 0.43914369, 0.43546065, 0.41802378,
0.42538987, 0.42406625, 0.44029464, 0.44029464, 0.42366345,
0.42878517, 0.43966163, 0.43569085, 0.44236633, 0.4424814 ])
In [27]:
y_test[0]
Out[27]:
0.45399086407968736
In [28]:
# 將巢狀串列(nested lists) 轉換為 Numpy Arrays, 以利 tensorflow 操作.
X_train = np.array(X_train)
X_test = np.array(X_test)
y_train = np.array(y_train)
y_test = np.array(y_test)
In [29]:
# 轉換前維度
X_train.shape # (2455, 60)
Out[29]:
(2455, 60)
In [30]:
y_train.shape # (2455,)
Out[30]:
(2455,)
In [31]:
X_test.shape # (482, 60)
Out[31]:
(482, 60)
In [32]:
y_test.shape # (482,)
Out[32]:
(482,)
In [33]:
# 維度轉換
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))
y_train = np.reshape(y_train, (-1,1))
y_test = np.reshape(y_test, (-1,1))
# 轉換後維度
print('X_train Shape: ', X_train.shape) # (2455, 60, 1)
print('y_train Shape: ', y_train.shape) # (2455, 1)
print('X_test Shape: ', X_test.shape) # (482, 60, 1)
print('y_test Shape: ', y_test.shape) # (482, 1)
X_train Shape: (2455, 60, 1) y_train Shape: (2455, 1) X_test Shape: (482, 60, 1) y_test Shape: (482, 1)
步驟 4:建立模型¶
In [34]:
# LSTM 定義
def define_model():
input1 = Input(shape=(window_size,1))
x = LSTM(units = 64, return_sequences=True)(input1)
x = Dropout(0.2)(x)
x = LSTM(units = 64, return_sequences=True)(x)
x = Dropout(0.2)(x)
x = LSTM(units = 64)(x)
x = Dropout(0.2)(x)
x = Dense(32, activation='softmax')(x)
dnn_output = Dense(1)(x)
model = Model(inputs=input1, outputs=[dnn_output])
model.compile(loss='mean_squared_error', optimizer='Nadam')
return model
# 建立LSTM模型
model = define_model()
# 模型摘要
model.summary()
Model: "functional"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ input_layer (InputLayer) │ (None, 60, 1) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ lstm (LSTM) │ (None, 60, 64) │ 16,896 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 60, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ lstm_1 (LSTM) │ (None, 60, 64) │ 33,024 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_1 (Dropout) │ (None, 60, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ lstm_2 (LSTM) │ (None, 64) │ 33,024 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_2 (Dropout) │ (None, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 32) │ 2,080 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 1) │ 33 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 85,057 (332.25 KB)
Trainable params: 85,057 (332.25 KB)
Non-trainable params: 0 (0.00 B)
In [35]:
# 模型訓練, 執行150世代, 須一些時間...
history = model.fit(X_train,
y_train,
epochs=150,
batch_size=32,
validation_split=0.1,
verbose=1)
Epoch 1/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 16s 73ms/step - loss: 0.0135 - val_loss: 0.0020 Epoch 2/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 0.0011 - val_loss: 0.0018 Epoch 3/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 8.2243e-04 - val_loss: 0.0015 Epoch 4/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 6.7939e-04 - val_loss: 0.0020 Epoch 5/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 6.3193e-04 - val_loss: 0.0010 Epoch 6/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 62ms/step - loss: 6.0963e-04 - val_loss: 8.3797e-04 Epoch 7/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 61ms/step - loss: 5.0481e-04 - val_loss: 0.0028 Epoch 8/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 5.2247e-04 - val_loss: 6.9251e-04 Epoch 9/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 4.8771e-04 - val_loss: 6.5426e-04 Epoch 10/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 60ms/step - loss: 4.3651e-04 - val_loss: 5.7864e-04 Epoch 11/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 4.5843e-04 - val_loss: 0.0022 Epoch 12/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 5.1377e-04 - val_loss: 5.7116e-04 Epoch 13/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 3.6027e-04 - val_loss: 5.8829e-04 Epoch 14/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 3.9247e-04 - val_loss: 0.0079 Epoch 15/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 60ms/step - loss: 7.3538e-04 - val_loss: 4.8902e-04 Epoch 16/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 3.8104e-04 - val_loss: 4.3934e-04 Epoch 17/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 3.0965e-04 - val_loss: 4.0908e-04 Epoch 18/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 3.5978e-04 - val_loss: 0.0017 Epoch 19/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 3.6622e-04 - val_loss: 4.4799e-04 Epoch 20/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 2.8564e-04 - val_loss: 5.5605e-04 Epoch 21/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 3.1798e-04 - val_loss: 4.4426e-04 Epoch 22/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 55ms/step - loss: 3.0281e-04 - val_loss: 5.6176e-04 Epoch 23/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 2.6238e-04 - val_loss: 0.0058 Epoch 24/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 55ms/step - loss: 4.9221e-04 - val_loss: 3.8660e-04 Epoch 25/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 2.5532e-04 - val_loss: 3.6969e-04 Epoch 26/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 60ms/step - loss: 2.3821e-04 - val_loss: 3.9765e-04 Epoch 27/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 61ms/step - loss: 2.2643e-04 - val_loss: 0.0015 Epoch 28/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 2.8469e-04 - val_loss: 0.0209 Epoch 29/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 0.0015 - val_loss: 4.5263e-04 Epoch 30/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.7566e-04 - val_loss: 3.4855e-04 Epoch 31/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.5061e-04 - val_loss: 3.1124e-04 Epoch 32/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.3381e-04 - val_loss: 2.8162e-04 Epoch 33/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 2.3776e-04 - val_loss: 3.6345e-04 Epoch 34/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.9509e-04 - val_loss: 0.0019 Epoch 35/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.7917e-04 - val_loss: 3.1317e-04 Epoch 36/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.3084e-04 - val_loss: 2.7915e-04 Epoch 37/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 2.0655e-04 - val_loss: 0.0056 Epoch 38/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 4.6409e-04 - val_loss: 0.0013 Epoch 39/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 2.8957e-04 - val_loss: 3.0516e-04 Epoch 40/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 55ms/step - loss: 2.0871e-04 - val_loss: 2.5150e-04 Epoch 41/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 2.1179e-04 - val_loss: 2.2896e-04 Epoch 42/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 2.0112e-04 - val_loss: 4.1794e-04 Epoch 43/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 2.1626e-04 - val_loss: 0.0027 Epoch 44/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 3.3055e-04 - val_loss: 7.2929e-04 Epoch 45/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 2.2482e-04 - val_loss: 4.2469e-04 Epoch 46/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.9307e-04 - val_loss: 2.5870e-04 Epoch 47/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.0700e-04 - val_loss: 2.8799e-04 Epoch 48/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.9328e-04 - val_loss: 2.2711e-04 Epoch 49/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.9957e-04 - val_loss: 3.1137e-04 Epoch 50/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.8402e-04 - val_loss: 2.3101e-04 Epoch 51/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 1.8249e-04 - val_loss: 0.0026 Epoch 52/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 2.3914e-04 - val_loss: 2.0625e-04 Epoch 53/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 1.7848e-04 - val_loss: 0.0181 Epoch 54/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 0.0012 - val_loss: 3.3777e-04 Epoch 55/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 60ms/step - loss: 2.3957e-04 - val_loss: 2.5069e-04 Epoch 56/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.9173e-04 - val_loss: 3.8854e-04 Epoch 57/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 2.1495e-04 - val_loss: 2.3455e-04 Epoch 58/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.7794e-04 - val_loss: 3.1975e-04 Epoch 59/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.7984e-04 - val_loss: 2.3835e-04 Epoch 60/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.8619e-04 - val_loss: 2.0049e-04 Epoch 61/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.7823e-04 - val_loss: 2.1113e-04 Epoch 62/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.7694e-04 - val_loss: 2.1338e-04 Epoch 63/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.3932e-04 - val_loss: 2.0681e-04 Epoch 64/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.6159e-04 - val_loss: 4.8196e-04 Epoch 65/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.9957e-04 - val_loss: 1.9958e-04 Epoch 66/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.4645e-04 - val_loss: 3.2801e-04 Epoch 67/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 2.0433e-04 - val_loss: 4.5516e-04 Epoch 68/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.7559e-04 - val_loss: 0.0029 Epoch 69/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 3.4577e-04 - val_loss: 2.0766e-04 Epoch 70/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.6237e-04 - val_loss: 1.9299e-04 Epoch 71/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.5945e-04 - val_loss: 1.7875e-04 Epoch 72/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 52ms/step - loss: 1.6372e-04 - val_loss: 2.0219e-04 Epoch 73/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 52ms/step - loss: 1.4172e-04 - val_loss: 7.3591e-04 Epoch 74/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.7783e-04 - val_loss: 5.8954e-04 Epoch 75/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 52ms/step - loss: 1.7945e-04 - val_loss: 2.1279e-04 Epoch 76/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.7307e-04 - val_loss: 1.8075e-04 Epoch 77/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.5993e-04 - val_loss: 1.8825e-04 Epoch 78/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 51ms/step - loss: 1.4542e-04 - val_loss: 9.1237e-04 Epoch 79/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.7726e-04 - val_loss: 6.3092e-04 Epoch 80/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 51ms/step - loss: 1.5891e-04 - val_loss: 2.0318e-04 Epoch 81/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 52ms/step - loss: 1.6473e-04 - val_loss: 2.3599e-04 Epoch 82/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.4441e-04 - val_loss: 2.4296e-04 Epoch 83/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.5360e-04 - val_loss: 3.0701e-04 Epoch 84/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3269e-04 - val_loss: 2.6922e-04 Epoch 85/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.6853e-04 - val_loss: 2.8925e-04 Epoch 86/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.4836e-04 - val_loss: 2.4284e-04 Epoch 87/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3646e-04 - val_loss: 3.2277e-04 Epoch 88/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.4615e-04 - val_loss: 0.0015 Epoch 89/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.8841e-04 - val_loss: 1.6791e-04 Epoch 90/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.3596e-04 - val_loss: 2.2799e-04 Epoch 91/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.6643e-04 - val_loss: 2.2634e-04 Epoch 92/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.4414e-04 - val_loss: 2.3572e-04 Epoch 93/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.4605e-04 - val_loss: 1.7744e-04 Epoch 94/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 52ms/step - loss: 1.4012e-04 - val_loss: 2.4749e-04 Epoch 95/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.2488e-04 - val_loss: 1.6517e-04 Epoch 96/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.4539e-04 - val_loss: 0.0025 Epoch 97/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 55ms/step - loss: 2.3382e-04 - val_loss: 6.0928e-04 Epoch 98/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.5916e-04 - val_loss: 1.6887e-04 Epoch 99/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.4051e-04 - val_loss: 1.4905e-04 Epoch 100/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.2507e-04 - val_loss: 3.2270e-04 Epoch 101/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.5692e-04 - val_loss: 3.3297e-04 Epoch 102/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.4735e-04 - val_loss: 0.0013 Epoch 103/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.8148e-04 - val_loss: 4.1339e-04 Epoch 104/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 54ms/step - loss: 1.5161e-04 - val_loss: 2.9922e-04 Epoch 105/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.4324e-04 - val_loss: 1.9280e-04 Epoch 106/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3656e-04 - val_loss: 3.3976e-04 Epoch 107/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.3601e-04 - val_loss: 2.1909e-04 Epoch 108/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.3582e-04 - val_loss: 2.2427e-04 Epoch 109/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.1513e-04 - val_loss: 1.7200e-04 Epoch 110/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.2777e-04 - val_loss: 2.4892e-04 Epoch 111/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3034e-04 - val_loss: 1.7855e-04 Epoch 112/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.4196e-04 - val_loss: 1.7495e-04 Epoch 113/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3059e-04 - val_loss: 3.1878e-04 Epoch 114/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 56ms/step - loss: 1.2429e-04 - val_loss: 2.6571e-04 Epoch 115/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.1911e-04 - val_loss: 1.7089e-04 Epoch 116/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.4085e-04 - val_loss: 2.1836e-04 Epoch 117/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.3066e-04 - val_loss: 3.0685e-04 Epoch 118/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.1508e-04 - val_loss: 2.8049e-04 Epoch 119/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.2290e-04 - val_loss: 2.3732e-04 Epoch 120/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.2022e-04 - val_loss: 1.8765e-04 Epoch 121/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3913e-04 - val_loss: 1.5724e-04 Epoch 122/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.3140e-04 - val_loss: 4.3183e-04 Epoch 123/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.5729e-04 - val_loss: 2.2192e-04 Epoch 124/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.2983e-04 - val_loss: 4.7211e-04 Epoch 125/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.4638e-04 - val_loss: 1.4660e-04 Epoch 126/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.3205e-04 - val_loss: 2.8801e-04 Epoch 127/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 1.5522e-04 - val_loss: 1.7456e-04 Epoch 128/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 57ms/step - loss: 1.1990e-04 - val_loss: 0.0056 Epoch 129/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 4.7419e-04 - val_loss: 0.0016 Epoch 130/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 2.1477e-04 - val_loss: 1.5832e-04 Epoch 131/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.4953e-04 - val_loss: 1.9310e-04 Epoch 132/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 58ms/step - loss: 1.3170e-04 - val_loss: 1.9438e-04 Epoch 133/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 59ms/step - loss: 1.4689e-04 - val_loss: 1.4154e-04 Epoch 134/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3876e-04 - val_loss: 1.3877e-04 Epoch 135/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.1663e-04 - val_loss: 1.5793e-04 Epoch 136/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.3193e-04 - val_loss: 1.3858e-04 Epoch 137/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.1990e-04 - val_loss: 1.7469e-04 Epoch 138/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.2281e-04 - val_loss: 0.0019 Epoch 139/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.9644e-04 - val_loss: 1.4447e-04 Epoch 140/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 57ms/step - loss: 1.2311e-04 - val_loss: 3.5130e-04 Epoch 141/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 56ms/step - loss: 1.2565e-04 - val_loss: 2.9709e-04 Epoch 142/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.2152e-04 - val_loss: 1.9254e-04 Epoch 143/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 56ms/step - loss: 1.4097e-04 - val_loss: 1.5970e-04 Epoch 144/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 5s 55ms/step - loss: 1.2158e-04 - val_loss: 1.6341e-04 Epoch 145/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 53ms/step - loss: 1.2063e-04 - val_loss: 4.8937e-04 Epoch 146/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.2041e-04 - val_loss: 6.8037e-04 Epoch 147/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.2991e-04 - val_loss: 2.2237e-04 Epoch 148/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 55ms/step - loss: 1.1744e-04 - val_loss: 1.6002e-04 Epoch 149/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.1543e-04 - val_loss: 4.4911e-04 Epoch 150/150 70/70 ━━━━━━━━━━━━━━━━━━━━ 4s 54ms/step - loss: 1.4177e-04 - val_loss: 3.4288e-04
步驟 5:建立評估與測試¶
In [36]:
# 模型評估
result = model.evaluate(X_test, y_test)
# 模型預測
y_pred = model.predict(X_test)
# MAPE (Mean Absolute Percentage Error) 平均絕對百分比誤差率
MAPE = mean_absolute_percentage_error(y_test, y_pred)
# 正確率
Accuracy = 1 - MAPE
# 顯示評估結果
print("Test Loss:", result)
print("Test MAPE:", MAPE) # 錯誤率 13.37%
print("Test Accuracy:", Accuracy) # 正確率 86.63%
16/16 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - loss: 0.0084 16/16 ━━━━━━━━━━━━━━━━━━━━ 1s 60ms/step Test Loss: 0.029436124488711357 Test MAPE: 0.15208566629513215 Test Accuracy: 0.8479143337048678
In [37]:
# 測試集評估結果的視覺化
# 轉換原刻度單位
y_test_true = scaler.inverse_transform(y_test)
y_test_pred = scaler.inverse_transform(y_pred)
# 使用 matplotlib 繪圖
# 設定繪圖區域
plt.figure(figsize=(15, 6), dpi=150)
# 設定繪圖區域顏色
plt.rcParams['axes.facecolor'] = '#FFD700'
# train data
plt.plot(df['Date'].iloc[:-test_size], scaler.inverse_transform(train_data), color='black', lw=2)
# actual test data
plt.plot(df['Date'].iloc[-test_size:], y_test_true, color='blue', lw=2)
# predicted test data
plt.plot(df['Date'].iloc[-test_size:], y_test_pred, color='red', lw=2)
# 主標題
plt.title('圖4. Model Performance on Gold Price Prediction', fontsize=15)
# X軸標題
plt.xlabel('Date', fontsize=12)
# Y軸標題
plt.ylabel('Price', fontsize=12)
# 圖例
plt.legend(['Training Data', 'Actual Test Data', 'Predicted Test Data'], loc='upper left', prop={'size': 15})
# 格線顏色
plt.grid(color='white')
plt.show()
# 結論:
# 本例使用 LSTM 模型進行黃金價格預測, 執行結果佳.
# 🏆 Loss: 13%
# 🏆 Accuracy: 87%
步驟 6:佈署應用¶
- 儲存模型
- 使用 Streamlit 建立互動式視覺化, 參考: https://youtu.be/-_zghs2qrIg?si=epkk5_CkidFhXDoU
- 改善線上執行效能
- 將此模型套用至工作資料集
3. 台灣股市,ETF下載¶
In [38]:
# 台灣ETF列表
# https://www.stockq.org/etf/
import yfinance as yf
import plotly.graph_objects as go
# 設定 plotly 繪圖結果顯示於瀏覽器
# 使用 Colab, Jupyter-notebook 不用設定此選項
# import plotly.io as pio
# pio.renderers.default='browser'
In [39]:
# mplfinance: matplotlib utilities for the visualization, and visual analysis, of financial data
# https://pypi.org/project/mplfinance/
# 上市: 台積電 '2330.TW'
# 上櫃: 崇友 '4506.TWO'
# ETF: '0050.TW'
# ETF: '0056.TW'
# ETF: '00878.TW'
# 擷取 元大台灣50ETF, 開始/結束資料
df = yf.download('0050.TW', start = '2008-10-03', end = '2023-12-31')
df
[*********************100%***********************] 1 of 1 completed
Out[39]:
| Price | Adj Close | Close | High | Low | Open | Volume |
|---|---|---|---|---|---|---|
| Ticker | 0050.TW | 0050.TW | 0050.TW | 0050.TW | 0050.TW | 0050.TW |
| Date | ||||||
| 2008-10-03 | 31.524755 | 44.610001 | 44.610001 | 44.610001 | 44.610001 | 0 |
| 2008-10-06 | 30.090202 | 42.580002 | 42.580002 | 42.580002 | 42.580002 | 0 |
| 2008-10-07 | 30.281006 | 42.849998 | 42.849998 | 42.849998 | 42.849998 | 0 |
| 2008-10-08 | 28.493116 | 40.320000 | 40.320000 | 40.320000 | 40.320000 | 0 |
| 2008-10-09 | 28.168049 | 39.860001 | 39.860001 | 39.860001 | 39.860001 | 0 |
| ... | ... | ... | ... | ... | ... | ... |
| 2023-12-25 | 129.794617 | 133.500000 | 133.800003 | 132.949997 | 133.000000 | 8080412 |
| 2023-12-26 | 130.523788 | 134.250000 | 134.399994 | 133.500000 | 133.500000 | 16816964 |
| 2023-12-27 | 131.982162 | 135.750000 | 135.899994 | 134.350006 | 134.399994 | 29950035 |
| 2023-12-28 | 131.982162 | 135.750000 | 136.000000 | 135.449997 | 135.750000 | 14848296 |
| 2023-12-29 | 131.690475 | 135.449997 | 136.000000 | 135.350006 | 135.699997 | 13111608 |
3746 rows × 6 columns
In [40]:
# 擷取 元大台灣50 ETF, 最近期間/頻率資料, 近5天(包括系統日期), 每隔1分鐘資料
df = yf.download('0050.TW', period = '5d', interval = '1m')
df
[*********************100%***********************] 1 of 1 completed
Out[40]:
| Price | Adj Close | Close | High | Low | Open | Volume |
|---|---|---|---|---|---|---|
| Ticker | 0050.TW | 0050.TW | 0050.TW | 0050.TW | 0050.TW | 0050.TW |
| Datetime | ||||||
| 2024-12-02 01:00:00+00:00 | 189.199997 | 189.199997 | 189.199997 | 188.500000 | 188.500000 | 0 |
| 2024-12-02 01:01:00+00:00 | 189.199997 | 189.199997 | 189.250000 | 189.100006 | 189.250000 | 76000 |
| 2024-12-02 01:02:00+00:00 | 189.199997 | 189.199997 | 189.250000 | 189.050003 | 189.100006 | 101000 |
| 2024-12-02 01:03:00+00:00 | 189.199997 | 189.199997 | 189.250000 | 189.100006 | 189.149994 | 83000 |
| 2024-12-02 01:04:00+00:00 | 189.300003 | 189.300003 | 189.449997 | 189.199997 | 189.199997 | 92000 |
| ... | ... | ... | ... | ... | ... | ... |
| 2024-12-06 05:20:00+00:00 | 195.449997 | 195.449997 | 195.649994 | 195.449997 | 195.649994 | 11541 |
| 2024-12-06 05:21:00+00:00 | 195.649994 | 195.649994 | 195.649994 | 195.550003 | 195.649994 | 38066 |
| 2024-12-06 05:22:00+00:00 | 195.550003 | 195.550003 | 195.649994 | 195.550003 | 195.550003 | 68743 |
| 2024-12-06 05:23:00+00:00 | 195.699997 | 195.699997 | 195.750000 | 195.550003 | 195.600006 | 49539 |
| 2024-12-06 05:24:00+00:00 | 195.699997 | 195.699997 | 195.750000 | 195.699997 | 195.699997 | 28060 |
1318 rows × 6 columns
In [41]:
# 建立財金資料擷取函數, 名稱為 getData
def getData(symbol, startDate, endDate):
# 擷取財金資料
data = yf.download(tickers = symbol, start = startDate, end = endDate)
# 將 index 轉換為 Date 資料行
data['Date'] = data.index
# 將 Date 移至第1欄位置
data = data[['Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']]
# 刪除 index 值
data = data.reset_index(drop=True)
# 回傳財金資料
return data
# 使用自訂函數 getData 擷取資料
df = getData('0050.TW', '2024-01-01', '2024-11-30')
df
[*********************100%***********************] 1 of 1 completed
Out[41]:
| Price | Date | Open | High | Low | Close | Adj Close | Volume |
|---|---|---|---|---|---|---|---|
| Ticker | 0050.TW | 0050.TW | 0050.TW | 0050.TW | 0050.TW | 0050.TW | |
| 0 | 2024-01-02 | 135.600006 | 135.949997 | 134.649994 | 134.899994 | 131.155746 | 5922076 |
| 1 | 2024-01-03 | 133.699997 | 133.899994 | 132.300003 | 132.550003 | 128.870987 | 13547475 |
| 2 | 2024-01-04 | 132.550003 | 132.750000 | 132.300003 | 132.500000 | 128.822372 | 4567593 |
| 3 | 2024-01-05 | 132.550003 | 132.949997 | 132.100006 | 132.149994 | 128.482071 | 3999671 |
| 4 | 2024-01-08 | 133.000000 | 133.600006 | 132.750000 | 132.750000 | 129.065430 | 9394720 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 215 | 2024-11-25 | 194.699997 | 194.750000 | 192.250000 | 192.350006 | 192.350006 | 7826663 |
| 216 | 2024-11-26 | 190.100006 | 190.300003 | 188.800003 | 189.850006 | 189.850006 | 11390822 |
| 217 | 2024-11-27 | 189.149994 | 189.449997 | 187.000000 | 187.100006 | 187.100006 | 14072686 |
| 218 | 2024-11-28 | 187.000000 | 187.100006 | 185.449997 | 186.800003 | 186.800003 | 13109114 |
| 219 | 2024-11-29 | 184.949997 | 188.000000 | 184.500000 | 187.250000 | 187.250000 | 8128220 |
220 rows × 7 columns
In [42]:
df.columns
Out[42]:
MultiIndex([( 'Date', ''),
( 'Open', '0050.TW'),
( 'High', '0050.TW'),
( 'Low', '0050.TW'),
( 'Close', '0050.TW'),
('Adj Close', '0050.TW'),
( 'Volume', '0050.TW')],
names=['Price', 'Ticker'])
In [43]:
# 使用 plotly 模組繪製 K 線圖, 預設值綠色表示上漲, 紅色表示下趺.
# K線圖採用歐美標準, 綠色表示上漲(收盤價高於開盤價), 紅色表示下趺(收盤價低於開盤價)
# 台灣等亞洲一般使用: 紅色表示上漲(收盤價高於開盤價), 綠色表示下趺(收盤價低於開盤價)
# K線(英語:Candlestick chart)又稱陰陽燭、蠟燭線.
# https://zh.wikipedia.org/wiki/K线
fig = go.Figure(data = [go.Candlestick(x=df[('Date', '')],
open = df[('Open', '0050.TW')],
high = df[('High', '0050.TW')],
low = df[('Low', '0050.TW')],
close = df[('Close', '0050.TW')],
increasing_line_color = '#FF4136', # 設定上漲為紅色
decreasing_line_color = '#3D9970' #
)])
# fig.update_layout(xaxis_rangeslider_visible=False)
fig.update_layout(xaxis_rangeslider_visible=False, # 取消成交量繪圖
xaxis_title="日期", # 設定X軸標題
yaxis_title="收盤價", # 設定Y軸標題
title={'text':'元大台灣50ETF收盤價走勢圖', 'x':0.5, 'y':0.95, 'xanchor':'center', 'yanchor':'top'})
fig.show()
進出場流程¶
- 選擇交易標的
- 進場判斷
- 出場判斷
交易策略¶
- 選擇交易標的: 考慮 0050
- 進場判斷: 當天K線是紅K,並且下引線是實體紅K的2倍.
- 出場判斷: 最少持有時間為3日, 3日過後只要當日紅K則出場.
參考資料¶
- Python:股票×ETF量化交易實戰105個活用技巧, 劉承彥著, 博碩文化.
- Python 與 Streamlit 模組 - 登山路線視覺化分析平台, https://youtu.be/-_zghs2qrIg?si=wlwT3Oj-F949-Gaw
- Gold Price Prediction | LSTM | 96% Accuracy, https://www.kaggle.com/code/farzadnekouei/gold-price-prediction-lstm-96-accuracy
In [ ]: